/* Copyright  2009
   @Author
   Richard Changde Yin            e-mail yinchangde@hotmail.com

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/**
 * @filename: multi_thread.cc
 */


#include <include/server_includes.h>
#include <include/atomics.h>
#include <include/gettext.h>
#include <include/error.h>
#include <include/plugin/scheduler.h>
#include <include/connect.h>
#include <include/sql_parse.h>
#include <include/session.h>
#include <include/connect.h>


#include <string>

using namespace std;

static uint32_t max_threads;

class Multi_thread_scheduler: public Scheduler
{
  tbb::atomic<uint32_t> thread_count;
  pthread_attr_t multi_thread_attrib;

public:
  Multi_thread_scheduler(uint32_t threads): Scheduler(threads)
  {
    thread_count= 0;
    /* Parameter for threads created for connections */
    (void) pthread_attr_init(&multi_thread_attrib);
    (void) pthread_attr_setdetachstate(&multi_thread_attrib,
                                       PTHREAD_CREATE_DETACHED);
    pthread_attr_setscope(&multi_thread_attrib, PTHREAD_SCOPE_SYSTEM);
    {
      struct sched_param tmp_sched_param;

      bzero(&tmp_sched_param,sizeof(tmp_sched_param));
      tmp_sched_param.sched_priority= WAIT_PRIOR;
      (void)pthread_attr_setschedparam(&multi_thread_attrib, &tmp_sched_param);
    }
  }

  ~Multi_thread_scheduler()
  {
    (void) pthread_mutex_lock(&LOCK_thread_count);
    while (thread_count)
    {
      pthread_cond_wait(&COND_thread_count, &LOCK_thread_count);
    }
    (void) pthread_mutex_unlock(&LOCK_thread_count);

    pthread_attr_destroy(&multi_thread_attrib);
  }

  virtual bool add_connection(Session *session)
  {
    int error;

    thread_count++;

    if ((error= pthread_create(&session->real_id, &multi_thread_attrib,
                              handle_one_connection, static_cast<void*>(session))))
      return true;

    return false;
  }


  /*
    End connection, in case when we are using 'no-threads'
  */

  virtual bool end_thread(Session *session, bool)
  {
    unlink_session(session);   /* locks LOCK_thread_count and deletes session */
    thread_count--;

    db_thread_end();
    pthread_exit(0);

    return true; // We should never reach this point
  }

  virtual uint32_t count(void)
  {
    return thread_count;
  }
};

static int init(void *p)
{
  Multi_thread_scheduler** sched= static_cast<Multi_thread_scheduler **>(p);

  *sched= new Multi_thread_scheduler(max_threads);

  return 0;
}

static int deinit(void *p)
{

  Multi_thread_scheduler *sched= static_cast<Multi_thread_scheduler *>(p);
  delete sched;

  return 0;
}

static COLLAPSAR_SYSVAR_UINT(max_threads, max_threads,
                           PLUGIN_VAR_RQCMDARG,
                           N_("Maximum number of user threads available."),
                           NULL, NULL, 2048, 1, 4048, 0);

static struct st_sql_sys_var* system_variables[]= {
  COLLAPSAR_SYSVAR(max_threads),
  NULL
};

collapsar_declare_plugin(multi_thread)
{
  COLLAPSAR_SCHEDULING_PLUGIN,
  "multi_thread",
  "0.1",
  "Richard C Yin",
  "One Thread Per Session Scheduler",
  PLUGIN_LICENSE_GPL,
  init,   /* Plugin Init */
  deinit, /* Plugin Deinit */
  NULL,   /* status variables */
  system_variables,   /* system variables */
  NULL    /* config options */
}
collapsar_declare_plugin_end;
